<i18n locale="zh-CN" lang="yaml">
Add API Auth  : 添加 API 认证
Setup API Auth: 配置 API 认证

Auth Type  : 认证类型
Auth Config: 认证配置
Func       : 认证函数
Note       : 备注

Fixed Fields   : 固定字段
Add Fixed Field: 添加固定字段
Field Name     : 字段名
Field Value    : 字段值
Users          : 用户
Add User       : 添加用户
Username       : 用户名

'Password (leave blank when not changing)': 密码（不修改时请留空）

Please select Func                     : 请选择认证函数
Func with a specific format is required: 必须指定特定格式的函数作为认证函数
Sample Code                            : Sample code
Show Sample Code                       : 显示Sample code

API Auth created: API 认证已创建
API Auth saved  : API 认证已保存
API Auth deleted: API 认证已删除

Are you sure you want to delete the API Auth?: 是否确认删除此 API 认证？

'Get / Check fields in Header': '获取/检查 Header 中字段'
'Get / Check fields in Query (e.g. http://you_domain/?auth-token=TOKEN)': '获取/检查 Query 中字段（如：http://you_domain/?auth-token=TOKEN）'
Throw Exception when authentication fails: 认证失败时，抛出 Exception 即可
Return True when authentication succeeds: 认证成功时，返回 True 即可
</i18n>

<!-- Generated by OpenCC START -->
<i18n locale="zh-HK" lang="yaml">
API Auth created: API 認證已創建
API Auth deleted: API 認證已刪除
API Auth saved: API 認證已保存
Add API Auth: 添加 API 認證
Add Fixed Field: 添加固定字段
Add User: 添加用户
Are you sure you want to delete the API Auth?: 是否確認刪除此 API 認證？
Auth Config: 認證配置
Auth Type: 認證類型
Field Name: 字段名
Field Value: 字段值
Fixed Fields: 固定字段
Func: 認證函數
Func with a specific format is required: 必須指定特定格式的函數作為認證函數
Get / Check fields in Header: 獲取/檢查 Header 中字段
Get / Check fields in Query (e.g. http://you_domain/?auth-token=TOKEN): 獲取/檢查 Query 中字段（如：http://you_domain/?auth-token=TOKEN）
Note: 備註
Password (leave blank when not changing): 密碼（不修改時請留空）
Please select Func: 請選擇認證函數
Return True when authentication succeeds: 認證成功時，返回 True 即可
Sample Code: Sample code
Setup API Auth: 配置 API 認證
Show Sample Code: 顯示Sample code
Throw Exception when authentication fails: 認證失敗時，拋出 Exception 即可
Username: 用户名
Users: 用户
</i18n>
<i18n locale="zh-TW" lang="yaml">
API Auth created: API 認證已建立
API Auth deleted: API 認證已刪除
API Auth saved: API 認證已儲存
Add API Auth: 新增 API 認證
Add Fixed Field: 新增固定欄位
Add User: 新增使用者
Are you sure you want to delete the API Auth?: 是否確認刪除此 API 認證？
Auth Config: 認證配置
Auth Type: 認證型別
Field Name: 欄位名
Field Value: 欄位值
Fixed Fields: 固定欄位
Func: 認證函式
Func with a specific format is required: 必須指定特定格式的函式作為認證函式
Get / Check fields in Header: 獲取/檢查 Header 中欄位
Get / Check fields in Query (e.g. http://you_domain/?auth-token=TOKEN): 獲取/檢查 Query 中欄位（如：http://you_domain/?auth-token=TOKEN）
Note: 備註
Password (leave blank when not changing): 密碼（不修改時請留空）
Please select Func: 請選擇認證函式
Return True when authentication succeeds: 認證成功時，返回 True 即可
Sample Code: Sample code
Setup API Auth: 配置 API 認證
Show Sample Code: 顯示Sample code
Throw Exception when authentication fails: 認證失敗時，丟擲 Exception 即可
Username: 使用者名稱
Users: 使用者
</i18n>
<!-- Generated by OpenCC END -->

<template>
  <el-dialog
    id="ScriptSetSetup"
    :visible.sync="show"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    width="750px">

    <template slot="title">
      {{ pageTitle }} <code class="text-main" v-if="pageMode === 'setup'">{{ data.title || C.API_AUTH_MAP.get(selectedType).name }}</code>
    </template>

    <el-container direction="vertical">
      <el-main>
        <div class="setup-form">
          <el-form ref="form" label-width="135px" :model="form" :rules="formRules">
            <el-form-item :label="$t('Auth Type')" prop="type" v-if="pageMode === 'add'">
              <el-select v-model="form.type" @change="switchType">
                <el-option v-for="opt in C.API_AUTH" :label="opt.name" :key="opt.key" :value="opt.key"></el-option>
              </el-select>
            </el-form-item>
            <el-form-item :label="$t('Auth Type')" v-else>
              <el-select v-model="selectedType" :disabled="true">
                <el-option :label="C.API_AUTH_MAP.get(selectedType).name" :value="selectedType"></el-option>
              </el-select>
            </el-form-item>

            <template v-if="selectedType">
              <el-form-item v-if="C.API_AUTH_MAP.get(selectedType).tips">
                <InfoBlock type="info" :title="C.API_AUTH_MAP.get(selectedType).tips" />
              </el-form-item>

              <el-form-item :label="$t('Title')">
                <el-input :placeholder="$t('Optional')"
                  maxlength="200"
                  v-model="form.title"></el-input>
              </el-form-item>

              <!-- Changable area -->

              <!-- Fixed fields config -->
              <template v-if="hasConfigField(selectedType, 'fields')">
                <el-form-item class="setup-divider" :label="$t('Fixed Fields')">
                  <el-divider></el-divider>
                </el-form-item>

                <template v-for="(fixedField, index) in form.configJSON.fields || []">
                  <el-form-item
                    class="fixed-field-location"
                    :label="`#${index + 1}`"
                    :key="`fieldLocation-${index}`"
                    :prop="`configJSON.fields.${index}.location`"
                    :rules="formRules_fixedFieldLocation">
                    <el-select
                      v-model="fixedField.location">
                      <el-option v-for="location in C.API_AUTH_FIXED_FIELD_LOCATION" :label="location.name" :key="location.key" :value="location.key"></el-option>
                    </el-select>

                    <!-- Delete button -->
                    <el-link type="danger" @click.prevent="removeFixedFieldItem(index)">{{ $t('Delete') }}</el-link>
                  </el-form-item>
                  <el-form-item
                    class="fixed-field"
                    :key="`fieldName-${index}`"
                    :prop="`configJSON.fields.${index}.name`"
                    :rules="formRules_fixedFieldName">
                    <el-input :placeholder="$t('Field Name')" v-model="fixedField.name"></el-input>
                  </el-form-item>
                  <el-form-item
                    class="fixed-field"
                    :key="`fieldValue-${index}`"
                    :prop="`configJSON.fields.${index}.value`"
                    :rules="formRules_fixedFieldValue">
                    <el-input :placeholder="$t('Field Value')" v-model="fixedField.value"></el-input>
                  </el-form-item>
                </template>
                <el-form-item>
                  <el-link type="primary" @click="addFixedFieldItem"><i class="fa fa-fw fa-plus"></i> {{ $t('Add Fixed Field') }}</el-link>
                </el-form-item>
              </template>

              <!-- HTTP auth config -->
              <template v-if="hasConfigField(selectedType, 'users')">
                <el-form-item class="setup-divider" :label="$t('Users')">
                  <el-divider></el-divider>
                </el-form-item>

                <template v-for="(user, index) in form.configJSON.users || []">
                  <el-form-item
                    class="http-auth"
                    :label="`#${index + 1}`"
                    :key="`username-${index}`"
                    :prop="`configJSON.users.${index}.username`"
                    :rules="formRules_httpAuthUsername">
                    <el-input :placeholder="$t('Username')" v-model="user.username"></el-input>

                    <!-- Delete button -->
                    <el-link type="danger" @click.prevent="removeHTTPAuthUser(index)">{{ $t('Delete') }}</el-link>
                  </el-form-item>
                  <el-form-item
                    class="http-auth"
                    :key="`password-${index}`"
                    :prop="`configJSON.users.${index}.password`"
                    :rules="formRules_httpAuthPassword">
                    <el-input :placeholder="$t('Password (leave blank when not changing)')"
                      v-model="user.password"></el-input>
                  </el-form-item>
                </template>
                <el-form-item>
                  <el-link type="primary" @click="addHTTPAuthUser"><i class="fa fa-fw fa-plus"></i> {{ $t('Add User') }}</el-link>
                </el-form-item>
              </template>

              <!-- Func auth config -->
              <template v-if="selectedType === 'func'">
                <el-form-item :label="$t('Func')" prop="funcId">
                  <el-cascader ref="funcCascader"
                    placeholder="--"
                    filterable
                    :filter-method="common.funcCascaderFilter"
                    v-model="form.funcId"
                    :options="funcCascader"
                    :props="{ expandTrigger: 'hover', emitPath: false, multiple: false }"></el-cascader>

                  <InfoBlock type="info" :title="$t('Func with a specific format is required')" />
                  <el-button @click="showAuthFuncSampleCode" type="text">{{ $t('Show Sample Code') }}</el-button>
                </el-form-item>
              </template>

              <!-- End of changable area -->

              <el-form-item :label="$t('Note')">
                <el-input :placeholder="$t('Optional')"
                  type="textarea"
                  resize="none"
                  :autosize="{minRows: 2}"
                  maxlength="5000"
                  v-model="form.note"></el-input>
              </el-form-item>

              <el-form-item class="setup-footer">
                <el-link type="danger" class="float-left" v-if="pageMode === 'setup'" @click="deleteData">{{ $t('Delete') }}</el-link>
                <el-button type="primary" v-prevent-re-click @click="submitData">{{ $t('Save') }}</el-button>
              </el-form-item>
            </template>
          </el-form>
        </div>
      </el-main>

      <LongTextDialog :title="$t('Sample Code')" mode="python" ref="longTextDialog" />
    </el-container>
  </el-dialog>
</template>

<script>
import LongTextDialog from '@/components/LongTextDialog'

export default {
  name: 'APIAuthSetup',
  components: {
    LongTextDialog,
  },
  watch: {
    show(val) {
      if (val && this.$refs.form) {
        this.$refs.form.clearValidate();
      }

      if (!val) {
        this.$root.$emit('reload.apiAuthList');
      }
    },
  },
  methods: {
    updateValidator(type) {
      if (this.$refs.form) this.$refs.form.clearValidate();

      let fieldMap = this.C.API_AUTH_MAP.get(type).configFields;
      if (!fieldMap) return;

      for (let f in fieldMap) if (fieldMap.hasOwnProperty(f)) {
        let opt = fieldMap[f];
        if (!opt) continue;

        let rule = this.formRules[`configJSON.${f}`];
        if (rule) {
          rule[0].required = !!opt.isRequired;
        }
      }
    },
    fillDefault(type) {
      let fieldMap = this.C.API_AUTH_MAP.get(type).configFields;
      if (!fieldMap) return;

      let nextConfigJSON = {};
      for (let f in fieldMap) if (fieldMap.hasOwnProperty(f)) {
        let opt = fieldMap[f];
        if (!opt) continue;

        if (this.T.notNothing(opt.default)) {
          nextConfigJSON[f] = opt.default;
        }
      }

      this.form.configJSON = nextConfigJSON;
    },
    switchType(type) {
      this.fillDefault(type);
      this.updateValidator(type);
    },
    async loadData(id) {
      if (!id) {
        this.pageMode = 'add';
        this.T.jsonClear(this.form);
        this.form.configJSON = {};
        this.data = {};

      } else {
        this.pageMode = 'setup';
        this.data.id = id;

        let apiRes = await this.T.callAPI_getOne('/api/v1/api-auth/do/list', this.data.id);
        if (!apiRes || !apiRes.ok) return;

        this.data = apiRes.data;

        let nextForm = {};
        Object.keys(this.form).forEach(f => nextForm[f] = this.data[f]);
        this.form = nextForm;

        this.updateValidator(this.data.type);
      }

      let funcList = await this.common.getFuncList();

      this.funcMap      = funcList.map;
      this.funcCascader = funcList.cascader;

      this.show = true;
    },
    async submitData() {
      try {
        await this.$refs.form.validate();
      } catch(err) {
        return console.error(err);
      }

      switch(this.pageMode) {
        case 'add':
          return await this.addData();
        case 'setup':
          return await this.modifyData();
      }
    },
    _getFormData() {
      let _formData = this.T.jsonCopy(this.form);
      if (_formData.configJSON) {
        for (let k in _formData.configJSON) {
          if (this.T.isNothing(_formData.configJSON[k])) {
            _formData.configJSON[k] = null;
          }
        }
      }
      return _formData;
    },
    async addData() {
      let _formData = this._getFormData();

      let apiRes = await this.T.callAPI('post', '/api/v1/api-auth/do/add', {
        body    : { data: _formData },
        feedback: { okMessage: this.$t('API Auth created') },
      });
      if (!apiRes || !apiRes.ok) return;

      this.$store.commit('updateHighlightedTableDataId', apiRes.data.id);
      this.show = false;
    },
    async modifyData() {
      let _formData = this._getFormData();
      delete _formData.id;
      delete _formData.type;

      let apiRes = await this.T.callAPI('post', '/api/v1/api-auth/:id/do/modify', {
        params  : { id: this.data.id },
        body    : { data: _formData },
        feedback: { okMessage: this.$t('API Auth saved') },
      });
      if (!apiRes || !apiRes.ok) return;

      this.$store.commit('updateHighlightedTableDataId', apiRes.data.id);
      this.show = false;
    },
    async deleteData() {
      if (!await this.T.confirm(this.$t('Are you sure you want to delete the API Auth?'))) return;

      let apiRes = await this.T.callAPI('/api/v1/api-auth/:id/do/delete', {
        params  : { id: this.data.id },
        feedback: { okMessage: this.$t('API Auth deleted') },
      });
      if (!apiRes || !apiRes.ok) return;

      this.show = false;
    },
    hasConfigField(type, field) {
      if (!this.C.API_AUTH_MAP.get(type) || !this.C.API_AUTH_MAP.get(type).configFields) {
        return false;
      }
      return (field in this.C.API_AUTH_MAP.get(type).configFields);
    },

    addFixedFieldItem() {
      if (this.T.isNothing(this.form.configJSON.fields)) {
        this.$set(this.form.configJSON, 'fields', []);
      }
      this.form.configJSON.fields.push({ location: '', name: '', value: '' });
    },
    removeFixedFieldItem(index) {
      this.form.configJSON.fields.splice(index, 1);
    },
    addHTTPAuthUser() {
      if (this.T.isNothing(this.form.configJSON.users)) {
        this.$set(this.form.configJSON, 'users', []);
      }
      this.form.configJSON.users.push({ username: '', password: '' });
    },
    removeHTTPAuthUser(index) {
      this.form.configJSON.users.splice(index, 1);
    },

    showAuthFuncSampleCode() {
      let sampleCode = `@DFF.API('My Auth Func')
def my_auth_func():
    # ${this.$t('Get / Check fields in Header')}
    try:
        is_valid_header = _DFF_HTTP_REQUEST['headers']['x-auth-token'] == 'TOKEN'
    except Exception as e:
        raise Exception('Missing \`x-auth-token\` in header')

    # ${this.$t('Get / Check fields in Query (e.g. http://you_domain/?auth-token=TOKEN)')}
    try:
        is_valid_query = _DFF_HTTP_REQUEST['query']['auth-token'] == 'TOKEN'
    except Exception as e:
        raise Exception('Missing \`auth-token\` in query')

    # ${this.$t('Throw Exception when authentication fails')}
    if not (is_valid_header and is_valid_query):
        raise Exception('Bad Auth Token')

    # ${this.$t('Return True when authentication succeeds')}
    return True`;
      this.$refs.longTextDialog.update(sampleCode);
    },
  },
  computed: {
    pageTitle() {
      const _map = {
        setup: this.$t('Setup API Auth'),
        add  : this.$t('Add API Auth'),
      };
      return _map[this.pageMode];
    },
    selectedType() {
      switch(this.pageMode) {
        case 'add':
          return this.form.type;

        case 'setup':
          return this.data.type;
      }
    },
  },
  props: {
  },
  data() {
    return {
      show    : false,
      pageMode: null,

      data        : {},
      funcMap     : {},
      funcCascader: [],

      form: {
        title     : null,
        type      : null,
        funcId    : null,
        configJSON: {},
        note      : null,
      },
      formRules: {
        type: [
          {
            trigger : 'blur',
            message : this.$t('Please input API Auth type'),
            required: true,
          },
        ],
        funcId: [
          {
            trigger : 'blur',
            message : this.$t('Please select Func'),
            required: true,
          },
        ],
      },
      formRules_fixedFieldLocation: {
        trigger: 'blur',
        message : this.$t('Please input location'),
        required: true,
      },
      formRules_fixedFieldName: {
        trigger: 'blur',
        message : this.$t('Please input field name'),
        required: true,
      },
      formRules_fixedFieldValue: {
        trigger: 'blur',
        message : this.$t('Please input field value'),
        required: true,
      },
      formRules_httpAuthUsername: {
        trigger: 'blur',
        message : this.$t('Please input HTTP Auth username'),
        required: true,
      },
      formRules_httpAuthPassword: {
        trigger: 'blur',
        message : this.$t('Please input HTTP Auth password'),
        required: false,
      },
    }
  },
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.fixed-field-location .el-select {
  width: 420px;
  display: inline-block;
}
.fixed-field .el-input,
.http-auth .el-input {
  width: 420px;
}

.fixed-field-location .el-link,
.fixed-field .el-link,
.http-auth .el-link {
  float: right;
}
</style>
